package com.data.hijack.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * 
 * @author husq
 *
 */
public final class BeanUtils {

    /**
     *
     * @param cache
     * @param props
     * @param <E>
     */
    public static <E> void createCacheObjectFromConfig(Map<String, E> cache, Properties props) {
        try {
            Set<Map.Entry<Object, Object>> set = props.entrySet();
            Iterator<Map.Entry<Object, Object>> iter = set.iterator();
            while (iter.hasNext()) {
                Map.Entry<Object, Object> me = iter.next();
                String key = (String)me.getKey();
                String value = (String)me.getValue();
                E instance = null;
                if (!StringUtils.isBlank(value)) {
                    Class<?> klass = Class.forName(value.trim());
                    instance = (E)instantiateClass(klass);
                }

                cache.put(key.trim(), instance);
            }
        } catch (Exception e) {
            cache.clear();
            throw new RuntimeException("Loading implementation configuration file exception", e);
        }
    }

    /**
     *
     * @param cache
     * @param props
     * @param <E>
     */
    public static <E> void createCacheClassFromConfig(Map<String, E> cache, Properties props) {
        try {
            Set<Map.Entry<Object, Object>> set = props.entrySet();
            Iterator<Map.Entry<Object, Object>> iter = set.iterator();
            while (iter.hasNext()) {
                Map.Entry<Object, Object> me = iter.next();
                String key = (String)me.getKey();
                String value = (String)me.getValue();
                if (StringUtils.isBlank(value)) {
                    continue;
                }
                E klass = (E)Class.forName(value.trim());
                cache.put(key.trim(), klass);
            }
        } catch (Exception e) {
            cache.clear();
            throw new RuntimeException("Loading implementation configuration file exception", e);
        }
    }

    public static Object createObject(String code, InputStream is) {
        Properties props = new Properties();
        try {
            props.load(is);
            String classStr = props.getProperty(code);
            if (classStr != null) {
                classStr = classStr.trim();
                Class<?> klass = Class.forName(classStr);

                return instantiateClass(klass);
            }
        } catch (Exception e) {
            throw new RuntimeException("Loading implementation configuration file exception", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                throw new RuntimeException(AppUtils.class.getName(), e);
            }
        }

        return null;
    }

    public static ClassLoader getClassLoader(Class<?> specified) {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
            ex.printStackTrace();
        }

        if (cl == null) {
            cl = specified.getClassLoader();
            if (cl == null) {
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable ex) {
                    ex.printStackTrace();
                }
            }
        }

        return cl;
    }

    public static <E> E instantiateClass(String classpath) {
        Class<E> clazz = null;
        try {
            clazz = (Class<E>)Class.forName(classpath);

        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Is the class path correctly?", e);
        }

        return instantiateClass(clazz);
    }

    public static <E> E instantiateClass(Class<E> clazz) throws RuntimeException {
        if (clazz == null) {
            throw new IllegalArgumentException("Class must not be null");
        }

        if (clazz.isInterface()) {
            throw new RuntimeException("Specified class is an interface");
        }
        try {
            return instantiateClass(clazz.getDeclaredConstructor(null), null);
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException("No default constructor found", ex);
        }
    }

    public static <E> E instantiateClass(Constructor<E> ctor, Object[] args) throws RuntimeException {
        if (ctor == null) {
            throw new IllegalArgumentException("Constructor must not be null");
        }
        try {
            if ((!Modifier.isPublic(ctor.getModifiers()))
                || (!Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))) {
                ctor.setAccessible(true);
            }
            return ctor.newInstance(args);
        } catch (InstantiationException ex) {
            throw new RuntimeException("Is it an abstract class?", ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException("Has the class definition changed? Is the constructor accessible?", ex);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException("Illegal arguments for constructor", ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException("Constructor threw exception", ex.getTargetException());
        }
    }
}
